package fr.jade.service.fm.reactor;

import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.objectweb.fractal.api.Component;
import org.objectweb.fractal.api.Interface;
import org.objectweb.fractal.api.NoSuchInterfaceException;
import org.objectweb.fractal.api.control.BindingController;
import org.objectweb.fractal.api.control.ContentController;
import org.objectweb.fractal.api.type.InterfaceType;

import fr.jade.base.JadeBaseComponent;
import fr.jade.service.fm.deployment.BindTask;
import fr.jade.service.fm.deployment.CleanBindingTask;
import fr.jade.service.fm.deployment.DeploymentTask;
import fr.jade.service.fm.deployment.NewComponentTask;
import fr.jade.util.FractalNavigator;
import fr.jade.util.FractalUtil;
import fr.jade.util.JadeException;

public class BindingPolicy extends JadeBaseComponent implements Policy{


  public int getPriorityLevel(){
    return 3;
  }
  
  public Set<DeploymentTask> execute(Map<Component, NewComponentTask> context, Set<Component> failedCmps_M, Set<Component> impactedCmps_M, Component newNode_M) throws JadeException {
    log("Binding Policy");
    Set<DeploymentTask> tasks= new HashSet<DeploymentTask>();
    Set<Component> cmps_M = new HashSet<Component>();
    cmps_M.addAll(failedCmps_M);
    cmps_M.addAll(impactedCmps_M);
    for (Component cmp_M : cmps_M) {
      for(Object objItf : cmp_M.getFcInterfaces()){
        checkAndCreateBindTask(context, tasks, cmp_M, objItf, failedCmps_M);
      }
      try{
        ContentController cc_M = FractalUtil.getOptContentController(cmp_M);
        for(Object objItf : cc_M.getFcInternalInterfaces()){
          checkAndCreateBindTask(context, tasks, cmp_M, objItf, failedCmps_M);
        }
      }catch(NoSuchInterfaceException ignored){
        // cmp_M is primitive : no internal interfaces
      }
    }
    return tasks;
  }
  
  protected void checkAndCreateBindTask(Map<Component, NewComponentTask> context, Set<DeploymentTask> tasks, Component cmp_M, Object objItf, Set<Component> failedCmps_M) {
    Interface itf = (Interface)objItf;
    InterfaceType itfType = (InterfaceType)itf.getFcItfType();
    if(itfType.isFcClientItf()){
      try{
        BindingController bc_M = FractalUtil.getBindingController(cmp_M);
        String itfName = itf.getFcItfName();
        Object serverObjItf = bc_M.lookupFc(itfName);
        if(serverObjItf!=null){
          Interface serverItf = (Interface)serverObjItf;
          String serverItfName = serverItf.getFcItfName();
          Component serverCmp_M = serverItf.getFcItfOwner();
          if(failedCmps_M.contains(cmp_M) || failedCmps_M.contains(serverCmp_M)){
            // create a BindTask only if the binding is not between two impacted components
            DeploymentTask t = null;
            if(failedCmps_M.contains(cmp_M)){
              // clean binding if source is impacted
              log(FractalNavigator.getName(cmp_M)+"."+itfName+"-> (unbind)");
              t = new CleanBindingTask(cmp_M, itfName);
              tasks.add(t);
            }
            // rebind
            log(FractalNavigator.getName(cmp_M)+"."+itfName+"->"+FractalNavigator.getName(serverCmp_M)+"."+serverItfName + "(rebind)");
            t = new BindTask(context.get(cmp_M), itfName, context.get(serverCmp_M), serverItfName);
            tasks.add(t);
          }else{
            log(FractalNavigator.getName(cmp_M)+"."+itfName+"->"+FractalNavigator.getName(serverCmp_M)+"."+serverItfName + "(ok)");
          }
        }else{
          log(FractalNavigator.getName(cmp_M)+"."+itfName+"-> X");
        }
      }catch(NoSuchInterfaceException ignored){
        // should not occur
        log("WARNING :" + FractalNavigator.getName(cmp_M)+"."+itf.getFcItfName());
        log(ignored);
      }
    }
  }


}
